/**
 * @file PES.c
 *
 * @author Luc Fourestier
 * @date 2009
 */

#include "PES.h"

#include "Global.h"
#include "Util.h"

#define MIN_PES_SIZE 6


/**
 * Create the PES_t structure
 *
 * @param Buffer: The incoming buffer containing the PES
 * @param BuffSize: in/out - the max buffer size and the actual parsed size as output
 * @param PES: The PES output pointer (must be allocated by caller)
 * @return errors
 */
int PES_Create(unsigned char * Buffer, unsigned int *Size, PES_t * PES)
{
#define step(n) Index += n

	int RetVal = OK;
	unsigned int Index = 0;

	if((PES != NULL) && (Buffer != NULL) && (Size != NULL) && (*Size >= MIN_PES_SIZE))
	{
		unsigned int Temp;

		PES->packet_start_code_prefix = GET_U64(Buffer, Index) & 0xffffff;
		step(3);
		PES->stream_id = GET_U8(Buffer, Index);
		step(1);
		PES->PES_packet_length = GET_U16(Buffer, Index);
		step(2);

		if ((PES->stream_id != program_stream_map)
		&& (PES->stream_id != padding_stream)
		&& (PES->stream_id != private_stream_2)
		&& (PES->stream_id != ECM)
		&& (PES->stream_id != EMM)
		&& (PES->stream_id != program_stream_directory)
		&& (PES->stream_id != DSMCC_stream)
		&& (PES->stream_id != ITU_E_stream))
		{
			PES->tag_10 = (GET_U8(Buffer, Index) & 0xc0) >> 6;
			PES->PES_scrambling_control = (GET_U8(Buffer, Index) & 0x30) >> 4;
			PES->PES_priority = (GET_U8(Buffer, Index) & 0x8) >> 3;
			PES->data_alignment_indicator = (GET_U8(Buffer, Index) & 0x4) >> 2;
			PES->copyright = (GET_U8(Buffer, Index) & 0x2) >> 1;
			PES->original_or_copy = GET_U8(Buffer, Index) & 0x1;
			step(1);
			PES->PTS_DTS_flags = (GET_U8(Buffer, Index) & 0xc0) >> 6;
			PES->ESCR_flag = (GET_U8(Buffer, Index) & 0x20) >> 5;
			PES->ES_rate_flag = (GET_U8(Buffer, Index) & 0x10) >> 4;
			PES->DSM_trick_mode_flag = (GET_U8(Buffer, Index) & 0x8) >> 3;
			PES->additional_copy_info = (GET_U8(Buffer, Index) & 0x4) >> 2;
			PES->PES_CRC_flag = (GET_U8(Buffer, Index) & 0x2) >> 1;
			PES->PES_extension_flag = GET_U8(Buffer, Index) & 0x1;
			step(1);
			PES->PES_header_data_length = GET_U8(Buffer, Index);
			step(1);

			if (PES->PTS_DTS_flags & 0x2) // PTS
			{
				unsigned long long Msb = GET_U8(Buffer, Index) & 0xe;
				step(1);
				unsigned long long Mid = GET_U16(Buffer, Index) & 0xfffe;
				step(2);
				unsigned long long Lsb = GET_U16(Buffer, Index) & 0xfffe;
				step(3);
				PES->PTS = (Msb << 29) + (Mid << 14) + (Lsb >> 1);
			}

			if (PES->PTS_DTS_flags & 0x1) // DTS
			{
				unsigned long long Msb = GET_U8(Buffer, Index) & 0xe;
				step(1);
				unsigned long long Mid = GET_U16(Buffer, Index) & 0xfffe;
				step(2);
				unsigned long long Lsb = GET_U16(Buffer, Index) & 0xfffe;
				step(3);
				PES->DTS = (Msb << 29) + (Mid << 14) + (Lsb >> 1);
			}

			if (PES->ESCR_flag == 1)
			{
//! @todo ESCR to be implemented
				step(6);
				PES->ESCR_base = 0;
				PES->ESCR_extension = 0;
			}

			if (PES->ES_rate_flag == 1)
			{
				PES->ES_rate = (GET_U64(Buffer, Index) & 0x7FFFFFFFFFFE0000LL) >> 9;
				step(3);
			}

//! @todo Rest to be implemented
		}

		// data byte
		Temp = 0;
		while((Index < (*Size)) && (Index < (PES->PES_packet_length+MIN_PES_SIZE)))
		{
			PES->PES_packet_data_byte[Temp] = GET_U8(Buffer, Index);
			step(1);
			Temp++;
		}
		PES->PES_packet_data_byte_length = Temp;

		*Size = Index;
	}
	else
	{
		RetVal = ERROR;
	}

	return(RetVal);

#undef step
}
